package timing.ukulele.portal.controller;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import timing.ukulele.common.data.ResponseData;
import timing.ukulele.data.portal.view.RoleVO;
import timing.ukulele.data.portal.view.SysResourceVO;
import timing.ukulele.data.portal.view.TenantUserRoleVO;
import timing.ukulele.data.user.view.UserVO;
import timing.ukulele.facade.portal.mvc.RoleControllerFacade;
import timing.ukulele.facade.user.feign.UserFeignFacade;
import timing.ukulele.portal.convertor.RoleConvertor;
import timing.ukulele.portal.dictionary.RoleTypeEnum;
import timing.ukulele.portal.persistent.SysRole;
import timing.ukulele.portal.persistent.SysTenant;
import timing.ukulele.portal.persistent.SysUserRole;
import timing.ukulele.portal.service.SysRoleService;
import timing.ukulele.portal.service.SysTenantService;
import timing.ukulele.share.UserLabelEnum;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@RestController
public final class RoleController implements RoleControllerFacade {
    private final SysRoleService service;
    private final UserFeignFacade userFacade;
    private final SysTenantService tenantService;

    @Autowired
    public RoleController(SysRoleService sysRoleService, UserFeignFacade userFacade, SysTenantService tenantService) {
        this.service = sysRoleService;
        this.userFacade = userFacade;
        this.tenantService = tenantService;
    }

    @Override
    public ResponseData<RoleVO> role(Long id) {
        if (id == null || id <= 0) {
            return ResponseData.paramError();
        }
        SysRole po = this.service.getById(id);
        if (po == null) {
            return ResponseData.success();
        }
        RoleVO vo = RoleConvertor.INSTANCE.p2v(po);
        vo.setTypeDescription(RoleTypeEnum.getDescriptionByCode(vo.getType()));
        return ResponseData.success(vo);
    }

    @Override
    public ResponseData<List<RoleVO>> getRoleByParam(Map<String, Object> map) {
        Collection<SysRole> poList = this.service.listByMap(map);
        if (CollectionUtils.isEmpty(poList)) {
            return ResponseData.success();
        }
        List<RoleVO> voList = new ArrayList<>(poList.size());
        poList.forEach(po -> {
            RoleVO vo = RoleConvertor.INSTANCE.p2v(po);
            vo.setTypeDescription(RoleTypeEnum.getDescriptionByCode(vo.getType()));
            voList.add(vo);
        });
        return ResponseData.success(voList);
    }

    @Override
    public ResponseData<List<RoleVO>> getTenantRole(Long tenantId, String code, String name, Boolean deleted) {
        LambdaQueryChainWrapper<SysRole> query = this.service.lambdaQuery().eq(SysRole::getTenantId, tenantId);
        query.like(StringUtils.hasText(code), SysRole::getCode, code);
        query.like(StringUtils.hasText(name), SysRole::getName, name);
        query.eq(deleted != null, SysRole::getDeleted, deleted);
        List<SysRole> poList = query.list();
        if (CollectionUtils.isEmpty(poList))
            return ResponseData.success(new ArrayList<>(0));
        List<RoleVO> voList = new ArrayList<>(poList.size());
        poList.forEach(po -> {
            RoleVO vo = RoleConvertor.INSTANCE.p2v(po);
            vo.setTypeDescription(RoleTypeEnum.getDescriptionByCode(vo.getType()));
            voList.add(vo);
        });
        return ResponseData.success(voList);
    }

    @Override
    public ResponseData<RoleVO> role(String currentUser, RoleVO role) {
        if (role == null || role.getId() != null || !StringUtils.hasText(role.getCode())) {
            return ResponseData.paramError();
        }
        if (!RoleTypeEnum.hasValue(role.getType())) {
            return ResponseData.paramError();
        }
        SysRole roleData = RoleConvertor.INSTANCE.v2p(role);
        roleData.setCode(roleData.getCode().toUpperCase());
        this.service.save(roleData);
        RoleVO vo = RoleConvertor.INSTANCE.p2v(roleData);
        return ResponseData.success(vo);
    }

    @Override
    public ResponseData<Boolean> roleUpdate(String currentUser, RoleVO role) {
        if (role == null || role.getId() == null) {
            return ResponseData.paramError();
        }
        if (!RoleTypeEnum.hasValue(role.getType())) {
            return ResponseData.paramError();
        }
        SysRole roleData = RoleConvertor.INSTANCE.v2p(role);
        roleData.setCode(null);
        roleData.setTenantId(null);
        roleData.setUpdateBy(currentUser);
        return ResponseData.success(this.service.saveOrUpdate(roleData));
    }

    @Override
    public ResponseData<Boolean> roleDel(String currentUser, Long id) {
        if (id == null || id <= 0) {
            return ResponseData.paramError();
        }
        this.service.lambdaUpdate().set(SysRole::getDeleted, Boolean.TRUE)
                .set(SysRole::getCreateBy, currentUser)
                .set(SysRole::getCreateTime, new Date()).eq(SysRole::getId, id).update();
        return ResponseData.success(Boolean.TRUE);
    }

    @Override
    public ResponseData<RoleVO> addRoleFollowCopyMenuAndResource(String currentUser, RoleVO role) {

        if (role == null || role.getId() != null || !StringUtils.hasText(role.getCode())) {
            return ResponseData.paramError();
        }
        if (!RoleTypeEnum.hasValue(role.getType())) {
            return ResponseData.paramError();
        }
        return this.service.addRoleFollowCopyMenuAndResource(currentUser, role);
    }

    @Override
    public ResponseData<List<RoleVO>> getUserRole(Long userId, Long tenantId) {
        if (userId == null || userId <= 0) {
            return ResponseData.paramError();
        }
        List<RoleVO> list = this.service.getUserRole(userId, tenantId);
        return ResponseData.success(list);
    }

    @Override
    public ResponseData<Boolean> deleteUserRole(String currentUser, Long userId, Long roleId, Long tenantId) {
        if (userId == null || userId <= 0) {
            return ResponseData.paramError();
        }
        return ResponseData.success(this.service.deleteUserRole(userId, roleId, tenantId));
    }

    @Override
    public ResponseData<Boolean> userRegistered(Long userId, String username) {
        return ResponseData.success(this.service.setUserRegisteredRole(userId, username));
    }

    @Override
    public ResponseData<Boolean> addUserRole(String currentUser, Long userId, String username, Long roleId, Long tenantId) {
        if (userId == null || userId <= 0 || roleId == null || roleId <= 0) {
            return ResponseData.paramError();
        }
        return ResponseData.success(this.service.addUserRole(userId, username, roleId, tenantId));
    }

    @Override
    public ResponseData<Boolean> changeTenantUserRole(Long tenantId, TenantUserRoleVO vo) {
        if (tenantId == null || vo == null || vo.getUserId() == null || !StringUtils.hasText(vo.getUsername()))
            return ResponseData.paramError();
        if (CollectionUtils.isEmpty(vo.getRoleList()))
            vo.setRoleList(new HashSet<>(0));
        if (vo.getOwner() != null && vo.getOwner()) {
            SysRole tenantOwner = this.service.lambdaQuery().eq(SysRole::getCode, "TENANT_OWNER").eq(SysRole::getTenantId, 0).select(SysRole::getId).last("limit 1").one();
            if (tenantOwner != null)
                vo.getRoleList().add(tenantOwner.getId());
        }
        if (vo.getAdmin() != null && vo.getAdmin()) {
            SysRole tenantAdmin = this.service.lambdaQuery().eq(SysRole::getCode, "TENANT_ADMIN").eq(SysRole::getTenantId, 0).select(SysRole::getId).last("limit 1").one();
            if (tenantAdmin != null)
                vo.getRoleList().add(tenantAdmin.getId());
        }
        return this.service.changeTenantUserRole(vo.getUserId(), vo.getUsername(), vo.getRoleList(), tenantId);
    }

    @Override
    public ResponseData<Boolean> addUserRoleAndTenant(String currentUser, Long userId, String username, Long roleId, Long tenantId) {
        if (userId == null || userId <= 0 || roleId == null || roleId <= 0) {
            return ResponseData.paramError();
        }
        return this.service.addUserRoleAndTenant(currentUser, userId, username, roleId, tenantId);
    }

    @GetMapping("/page/candidate/{current}/{size}")
    public ResponseData<IPage<RoleVO>> getUserCandidateRolePage(@PathVariable(name = "current") int current,
                                                                @PathVariable(value = "size") int size,
                                                                @RequestParam(value = "username") String username,
                                                                @RequestParam(value = "code", required = false) String code,
                                                                @RequestParam(value = "name", required = false) String name) {
        ResponseData<UserVO> userResponse = userFacade.getUserByUserName(username);
        if (userResponse == null || userResponse.getData() == null) {
            return ResponseData.error(
                    userResponse == null ? "查询用户失败" : StringUtils.hasText(userResponse.getMessage())
                            ? userResponse.getMessage() : "查询用户失败");
        }
        UserVO user = userResponse.getData();
        String label = user.getLabel();
        Boolean has = UserLabelEnum.hasLabel(label);
        if (!has) {
            return ResponseData.error("用户类型有误");
        }
        int type = RoleTypeEnum.BUSINESS.getCode();
        if (UserLabelEnum.ADMIN.getLabel().equalsIgnoreCase(label) || UserLabelEnum.SUPER.getLabel().equalsIgnoreCase(label)) {
            type = RoleTypeEnum.SYSTEM.getCode();
        }
        if (size == 0) {
            size = 10;
        }
        if (current == 0) {
            current = 1;
        }
        return ResponseData.success(this.service.getPage(code, name, null, type, false, current, size));
    }

    @GetMapping("/page/{current}/{size}")
    public ResponseData<IPage<RoleVO>> getPage(@PathVariable(name = "current") int current,
                                               @PathVariable(value = "size") int size,
                                               @RequestParam(value = "code", required = false) String code,
                                               @RequestParam(value = "name", required = false) String name,
                                               @RequestParam(value = "description", required = false) String description,
                                               @RequestParam(value = "type", required = false) Integer type,
                                               @RequestParam(value = "deleted", required = false) Boolean deleted) {
        if (size == 0) {
            size = 10;
        }
        if (current == 0) {
            current = 1;
        }
        IPage<RoleVO> page = this.service.getPage(code, name, description, type, deleted, current, size);
        if (CollectionUtils.isEmpty(page.getRecords()))
            return ResponseData.success(page);
        List<RoleVO> records = page.getRecords();
        Set<Long> tenants = records.stream().map(RoleVO::getTenantId).collect(Collectors.toSet());
        List<SysTenant> list = this.tenantService.lambdaQuery().in(SysTenant::getId, tenants).select(SysTenant::getId, SysTenant::getName).list();
        if (CollectionUtils.isEmpty(list))
            return ResponseData.success(page);
        Map<Long, SysTenant> tenantMap = list.stream().collect(Collectors.toMap(SysTenant::getId, Function.identity(), (oldValue, newValue) -> newValue));
        records.forEach(item -> {
            SysTenant tenant = tenantMap.get(item.getTenantId());
            if (tenant != null) {
                item.setTenantName(tenant.getName());
            }
        });
        return ResponseData.success(page);
    }

    @GetMapping("/page/user/{current}/{size}")
    public ResponseData<IPage<SysUserRole>> getPage(@PathVariable(name = "current") int current,
                                                    @PathVariable(value = "size") int size,
                                                    @RequestParam(value = "roleId") Long roleId,
                                                    @RequestParam(value = "username", required = false) String username) {
        if (size == 0) {
            size = 10;
        }
        if (current == 0) {
            current = 1;
        }
        IPage<SysUserRole> page = this.service.getRoleUserPage(roleId, username, current, size);
        return ResponseData.success(page);
    }


    @Override
    public ResponseData<List<SysResourceVO>> findByRole(Long roleId) {
        List<SysResourceVO> list = this.service.findByRole(roleId);
        return ResponseData.success(list);
    }

    @Override
    public ResponseData<Map<String, Set<String>>> findByUsername(String username, Long tenantId) {
        Map<String, Set<String>> userResource = this.service.getUserResource(username, tenantId);
        return ResponseData.success(userResource);
    }

    @Override
    public ResponseData<List<SysResourceVO>> findByRoles(List<Long> roleIdList) {
        List<SysResourceVO> list = this.service.findByRoles(roleIdList);
        return ResponseData.success(list);
    }

    @Override
    public ResponseData<List<RoleVO>> findRolesByIds(List<Long> roleIdList) {
        List<RoleVO> list = this.service.findRolesByIds(roleIdList);
        return ResponseData.success(list);
    }

    @Override
    public ResponseData<Boolean> addRoleResource(Long roleId, List<Long> resourceIdList) {
        this.service.addRoleResource(roleId, resourceIdList);
        return ResponseData.success(Boolean.TRUE);
    }

    @Override
    public ResponseData<Boolean> deleteRoleResource(Long roleId, Long resourceId) {
        this.service.deleteRoleResource(roleId, resourceId);
        return ResponseData.success(Boolean.TRUE);
    }

    @GetMapping("/resource/page/{roleId}/{current}/{size}")
    public ResponseData<IPage<SysResourceVO>> getResourcePage(@PathVariable(name = "current") int current,
                                                              @PathVariable(value = "size") int size,
                                                              @PathVariable(value = "roleId") Long roleId,
                                                              @RequestParam(value = "groupId", required = false) Long groupId,
                                                              @RequestParam(value = "resource", required = false) String resource,
                                                              @RequestParam(value = "remark", required = false) String remark,
                                                              @RequestParam(value = "method", required = false) String method,
                                                              @RequestParam(value = "deleted", required = false) Boolean deleted) {
        if (size == 0) {
            size = 10;
        }
        if (current == 0) {
            current = 1;
        }
        return ResponseData.success(this.service.getResourcePage(roleId, groupId, resource, remark, method, deleted, current, size));
    }

}
